<?php

namespace App\Services;

use App\Models\User;
use Illuminate\Support\Facades\Redis;
use Tymon\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Str;

class TokenService
{
    protected $prefix;
    protected $ttl;

    public function __construct()
    {
        $this->prefix = config('jwt.redis_prefix', 'admin:token:');
        $this->ttl = config('jwt.redis_ttl', 86400);
    }

    /**
     * 生成 JWT Token
     *
     * @param User $user
     * @param string|null $ip
     * @param string|null $userAgent
     * @param bool $forceNew 是否强制生成新token（忽略复用逻辑）
     * @return string
     */
    public function generate(User $user, $ip = null, $userAgent = null, bool $forceNew = false): string
    {
        // 如果不是强制生成新token，尝试复用现有token
        if (!$forceNew) {
            $existingToken = $this->getValidToken($user->id, $ip, $userAgent);
            if ($existingToken) {
                // 刷新token过期时间
                $this->refresh($user->id, $existingToken);
                return $existingToken;
            }
        }
        
        // 生成新的 JWT Token
        $token = JWTAuth::fromUser($user);
        
        // 存储到 Redis
        $this->store($user->id, $token, $ip, $userAgent);
        
        return $token;
    }
    
    /**
     * 获取用户的有效token（如果存在且匹配IP和设备）
     *
     * @param int $userId
     * @param string|null $ip
     * @param string|null $userAgent
     * @return string|null
     */
    public function getValidToken($userId, $ip = null, $userAgent = null): ?string
    {
        try {
            $userTokensKey = $this->getUserTokensKey($userId);
            $tokens = Redis::smembers($userTokensKey);
            
            if (empty($tokens)) {
                return null;
            }
            
            // 查找匹配IP和User-Agent的token
            foreach ($tokens as $token) {
                $tokenInfo = $this->getTokenInfo($userId, $token);
                
                if (!$tokenInfo) {
                    continue;
                }
                
                // 如果提供了IP，检查IP是否匹配
                if ($ip && isset($tokenInfo['ip'])) {
                    if ($tokenInfo['ip'] !== $ip) {
                        continue;
                    }
                }
                
                // 如果提供了User-Agent，检查User-Agent是否匹配
                if ($userAgent && isset($tokenInfo['user_agent'])) {
                    if ($tokenInfo['user_agent'] !== substr($userAgent, 0, 255)) {
                        continue;
                    }
                }
                
                // 验证token是否仍然有效
                if ($this->validate($userId, $token)) {
                    return $token;
                }
            }
        } catch (\Exception $e) {
            \Log::warning('Failed to get valid token: ' . $e->getMessage());
        }
        
        return null;
    }
    
    /**
     * 获取用户的所有有效token数量
     *
     * @param int $userId
     * @return int
     */
    public function getTokenCount($userId): int
    {
        try {
            $userTokensKey = $this->getUserTokensKey($userId);
            $tokens = Redis::smembers($userTokensKey);
            
            if (empty($tokens)) {
                return 0;
            }
            
            // 统计有效的token数量
            $count = 0;
            foreach ($tokens as $token) {
                if ($this->validate($userId, $token)) {
                    $count++;
                }
            }
            
            return $count;
        } catch (\Exception $e) {
            \Log::warning('Failed to get token count: ' . $e->getMessage());
            return 0;
        }
    }
    
    /**
     * 清理用户的无效token
     *
     * @param int $userId
     * @return int 清理的token数量
     */
    public function cleanInvalidTokens($userId): int
    {
        try {
            $userTokensKey = $this->getUserTokensKey($userId);
            $tokens = Redis::smembers($userTokensKey);
            
            if (empty($tokens)) {
                return 0;
            }
            
            $cleanedCount = 0;
            $validTokens = [];
            
            foreach ($tokens as $token) {
                if ($this->validate($userId, $token)) {
                    $validTokens[] = $token;
                } else {
                    // 删除无效token
                    $this->remove($token, $userId);
                    $cleanedCount++;
                }
            }
            
            return $cleanedCount;
        } catch (\Exception $e) {
            \Log::warning('Failed to clean invalid tokens: ' . $e->getMessage());
            return 0;
        }
    }
    
    /**
     * 获取用户的所有token列表（带创建时间信息）
     *
     * @param int $userId
     * @return array token信息列表，包含token和创建时间
     */
    public function getUserTokens($userId): array
    {
        try {
            $userTokensKey = $this->getUserTokensKey($userId);
            $tokens = Redis::smembers($userTokensKey);
            
            if (empty($tokens)) {
                return [];
            }
            
            $tokenList = [];
            foreach ($tokens as $token) {
                $tokenInfo = $this->getTokenInfo($userId, $token);
                if ($tokenInfo && $this->validate($userId, $token)) {
                    $tokenList[] = [
                        'token' => $token,
                        'created_at' => $tokenInfo['created_at'] ?? null,
                        'ip' => $tokenInfo['ip'] ?? null,
                        'user_agent' => $tokenInfo['user_agent'] ?? null,
                    ];
                }
            }
            
            // 按创建时间排序
            usort($tokenList, function ($a, $b) {
                $timeA = $a['created_at'] ? strtotime($a['created_at']) : 0;
                $timeB = $b['created_at'] ? strtotime($b['created_at']) : 0;
                return $timeA - $timeB;
            });
            
            return $tokenList;
        } catch (\Exception $e) {
            \Log::warning('Failed to get user tokens: ' . $e->getMessage());
            return [];
        }
    }
    
    /**
     * 删除用户最旧的token
     *
     * @param int $userId
     * @return bool 是否成功删除
     */
    public function removeOldestToken($userId): bool
    {
        try {
            $tokenList = $this->getUserTokens($userId);
            
            if (empty($tokenList)) {
                return false;
            }
            
            // 获取最旧的token（已按创建时间排序）
            $oldestToken = $tokenList[0]['token'];
            
            // 删除最旧的token
            $this->remove($oldestToken, $userId);
            
            return true;
        } catch (\Exception $e) {
            \Log::warning('Failed to remove oldest token: ' . $e->getMessage());
            return false;
        }
    }

    public function store($userId, $token, $ip = null, $userAgent = null)
    {
        try {
            $key = $this->getKey($userId, $token);
            $data = [
                'user_id' => $userId,
                'token' => $token,
                'created_at' => now()->toDateTimeString(),
            ];
            
            // 存储IP地址和设备信息（用于安全验证）
            if ($ip) {
                $data['ip'] = $ip;
            }
            if ($userAgent) {
                $data['user_agent'] = substr($userAgent, 0, 255); // 限制长度
            }
            
            Redis::setex($key, $this->ttl, json_encode($data));

            // 记录用户的所有token
            $userTokensKey = $this->getUserTokensKey($userId);
            Redis::sadd($userTokensKey, $token);
            Redis::expire($userTokensKey, $this->ttl);
        } catch (\Exception $e) {
            // Redis 连接失败时记录日志，但不阻止 token 生成
            // JWT token 本身是自包含的，不依赖 Redis 也能工作
            \Log::warning('Failed to store token in Redis: ' . $e->getMessage(), [
                'user_id' => $userId,
                'error' => $e->getMessage()
            ]);
        }
    }
    
    /**
     * 获取token信息
     *
     * @param int $userId
     * @param string $token
     * @return array|null
     */
    public function getTokenInfo($userId, $token): ?array
    {
        try {
            $key = $this->getKey($userId, $token);
            $data = Redis::get($key);
            
            if ($data) {
                return json_decode($data, true);
            }
        } catch (\Exception $e) {
            \Log::warning('Failed to get token info from Redis: ' . $e->getMessage());
        }
        
        return null;
    }
    
    /**
     * 验证IP地址是否匹配
     *
     * @param int $userId
     * @param string $token
     * @param string $ip
     * @return bool
     */
    public function validateIp($userId, $token, $ip): bool
    {
        try {
            $tokenInfo = $this->getTokenInfo($userId, $token);
            
            if (!$tokenInfo || !isset($tokenInfo['ip'])) {
                // 如果没有存储IP，则允许（向后兼容）
                return true;
            }
            
            // 允许IP地址匹配或IP段匹配（根据安全策略调整）
            return $tokenInfo['ip'] === $ip;
        } catch (\Exception $e) {
            // Redis 不可用时，默认允许（向后兼容）
            \Log::warning('Failed to validate IP, allowing access: ' . $e->getMessage());
            return true;
        }
    }

    public function validate($userId, $token): bool
    {
        try {
            $key = $this->getKey($userId, $token);
            return Redis::exists($key) > 0;
        } catch (\Exception $e) {
            // Redis 不可用时，默认返回 true（允许 token 验证通过）
            // 因为 JWT token 本身是有效的
            \Log::warning('Redis unavailable for token validation, allowing token: ' . $e->getMessage());
            return true;
        }
    }

    public function remove($token, $userId = null)
    {
        try {
            // 如果没有提供 userId，尝试从 token 中解析
            if (!$userId) {
                try {
                    $payload = JWTAuth::setToken($token)->getPayload();
                    $userId = $payload->get('sub');
                } catch (\Exception $e) {
                    // 如果解析失败，记录日志但不抛出异常
                    \Log::warning('Failed to parse token for removal: ' . $e->getMessage());
                    return;
                }
            }
            
            if ($userId) {
                $key = $this->getKey($userId, $token);
                Redis::del($key);

                $userTokensKey = $this->getUserTokensKey($userId);
                Redis::srem($userTokensKey, $token);
            }
        } catch (\Exception $e) {
            // Redis 不可用时，只记录日志，不抛出异常
            \Log::warning('Failed to remove token from Redis: ' . $e->getMessage());
        }
    }

    public function removeAll($userId): void
    {
        try {
            $userTokensKey = $this->getUserTokensKey($userId);
            $tokens = Redis::smembers($userTokensKey);

            if (!empty($tokens)) {
                $keys = [];
                foreach ($tokens as $token) {
                    $keys[] = $this->getKey($userId, $token);
                }
                
                // 批量删除，提高性能
                if (!empty($keys)) {
                    Redis::del($keys);
                }
            }

            Redis::del($userTokensKey);
        } catch (\Exception $e) {
            \Log::warning('Failed to remove all tokens from Redis: ' . $e->getMessage());
        }
    }

    public function refresh($userId, $token): bool
    {
        try {
            $key = $this->getKey($userId, $token);
            if (Redis::exists($key) > 0) {
                Redis::expire($key, $this->ttl);
                return true;
            }
            return false;
        } catch (\Exception $e) {
            \Log::warning('Failed to refresh token in Redis: ' . $e->getMessage());
            return false;
        }
    }

    protected function getKey($userId, $token)
    {
        return $this->prefix . $userId . ':' . md5($token);
    }

    protected function getUserTokensKey($userId)
    {
        return $this->prefix . 'user:' . $userId . ':tokens';
    }
}

